From f4d6d02135e06f7e9fa63e6f48692b1a7abfb45d Mon Sep 17 00:00:00 2001 From: Carl Lerche Date: Wed, 9 Apr 2014 18:07:47 -0700 Subject: [PATCH] Beginnings of a dependency resolver --- Makefile | 19 +++++--- libs/hamcrest-rust | 2 +- src/cargo/core/dependency.rs | 16 +++++++ src/cargo/core/mod.rs | 11 +++++ src/cargo/core/package.rs | 25 ++++++++++ src/cargo/core/registry.rs | 37 +++++++++++++++ src/cargo/core/resolver.rs | 90 ++++++++++++++++++++++++++++++++++++ src/cargo/mod.rs | 7 ++- tests/support.rs | 8 ++-- tests/test_cargo_compile.rs | 2 +- 10 files changed, 204 insertions(+), 13 deletions(-) create mode 100644 src/cargo/core/dependency.rs create mode 100644 src/cargo/core/package.rs create mode 100644 src/cargo/core/registry.rs create mode 100644 src/cargo/core/resolver.rs diff --git a/Makefile b/Makefile index 6d50d154d..683f4ecfd 100644 --- a/Makefile +++ b/Makefile @@ -27,7 +27,7 @@ $(HAMMER): $(wildcard libs/hammer.rs/src/*.rs) $(TOML): $(wildcard libs/rust-toml/src/toml/*.rs) cd libs/rust-toml && make -$(HAMCREST): $(wildcard libs/hamcrest-rust/src/hamcrest/*.rs) +$(HAMCREST): $(shell find libs/hamcrest-rust/src/hamcrest -name '*.rs') cd libs/hamcrest-rust && make # === Cargo @@ -49,13 +49,20 @@ $(BIN_TARGETS): target/%: src/bin/%.rs $(HAMMER) $(TOML) $(LIBCARGO) TEST_SRC = $(wildcard tests/*.rs) TEST_DEPS = $(DEPS) -L libs/hamcrest-rust/target -target/tests: $(BIN_TARGETS) $(HAMCREST) $(TEST_SRC) - $(RUSTC) --test --crate-type=lib $(TEST_DEPS) -Ltarget --out-dir target tests/tests.rs +target/tests/test-integration: $(BIN_TARGETS) $(HAMCREST) $(TEST_SRC) + $(RUSTC) --test --crate-type=lib $(TEST_DEPS) -Ltarget -o $@ tests/tests.rs -test-integration: target/tests +target/tests/test-unit: $(HAMCREST) $(SRC) $(HAMMER) + mkdir -p target/tests + $(RUSTC) --test $(RUSTC_FLAGS) $(TEST_DEPS) -o $@ src/cargo/mod.rs + +test-unit: target/tests/test-unit + target/tests/test-unit + +test-integration: target/tests/test-integration CARGO_BIN_PATH=$(PWD)/target/ $< -test: test-integration +test: test-unit test-integration clean: rm -rf target @@ -66,7 +73,7 @@ distclean: clean cd libs/rust-toml && make clean # Setup phony tasks -.PHONY: all clean distclean test test-integration libcargo +.PHONY: all clean distclean test test-unit test-integration libcargo # Disable unnecessary built-in rules .SUFFIXES: diff --git a/libs/hamcrest-rust b/libs/hamcrest-rust index 634495ea0..54ef9a306 160000 --- a/libs/hamcrest-rust +++ b/libs/hamcrest-rust @@ -1 +1 @@ -Subproject commit 634495ea08f3c3b019dc4e6d464ada83b00f926f +Subproject commit 54ef9a3064d85c9756a9c183087a9e4e28056651 diff --git a/src/cargo/core/dependency.rs b/src/cargo/core/dependency.rs new file mode 100644 index 000000000..4425af0cf --- /dev/null +++ b/src/cargo/core/dependency.rs @@ -0,0 +1,16 @@ + +// TODO: add version restrictions +#[deriving(Clone,Eq,Show)] +pub struct Dependency { + name: ~str, +} + +impl Dependency { + pub fn new(name: &str) -> Dependency { + Dependency { name: name.to_owned() } + } + + pub fn get_name<'a>(&'a self) -> &'a str { + self.name.as_slice() + } +} diff --git a/src/cargo/core/mod.rs b/src/cargo/core/mod.rs index e13802c89..56b552a66 100644 --- a/src/cargo/core/mod.rs +++ b/src/cargo/core/mod.rs @@ -1,8 +1,19 @@ +pub use self::dependency::Dependency; +pub use self::registry::{ + Registry, + MemRegistry}; + pub use self::manifest::{ Manifest, Project, LibTarget, ExecTarget}; +pub use self::package::Package; + +mod dependency; mod manifest; +mod package; +mod registry; +mod resolver; diff --git a/src/cargo/core/package.rs b/src/cargo/core/package.rs new file mode 100644 index 000000000..9bd4a219c --- /dev/null +++ b/src/cargo/core/package.rs @@ -0,0 +1,25 @@ +use std::vec::Vec; +use core; + +/** + * Represents a rust library internally to cargo. This will things like where + * on the local system the code is located, it's remote location, dependencies, + * etc.. + * + * This differs from core::Project + */ +#[deriving(Clone,Eq,Show)] +pub struct Package { + name: ~str, + deps: Vec +} + +impl Package { + pub fn new(name: &str, deps: &Vec) -> Package { + Package { name: name.to_owned(), deps: deps.clone() } + } + + pub fn get_name<'a>(&'a self) -> &'a str { + self.name.as_slice() + } +} diff --git a/src/cargo/core/registry.rs b/src/cargo/core/registry.rs new file mode 100644 index 000000000..a5efe14ba --- /dev/null +++ b/src/cargo/core/registry.rs @@ -0,0 +1,37 @@ +use std::vec::Vec; + +use core::{ + // Dependency, + Package}; + +pub trait Registry { + fn query<'a>(&'a self, name: &str) -> Vec<&'a Package>; +} + +/* + * + * ===== Temporary for convenience ===== + * + */ + +pub struct MemRegistry { + packages: Vec +} + +impl MemRegistry { + pub fn new(packages: &Vec) -> MemRegistry { + MemRegistry { packages: packages.clone() } + } + + pub fn empty() -> MemRegistry { + MemRegistry { packages: Vec::new() } + } +} + +impl Registry for MemRegistry { + fn query<'a>(&'a self, name: &str) -> Vec<&'a Package> { + self.packages.iter() + .filter(|pkg| name == pkg.get_name()) + .collect() + } +} diff --git a/src/cargo/core/resolver.rs b/src/cargo/core/resolver.rs new file mode 100644 index 000000000..fcf167eed --- /dev/null +++ b/src/cargo/core/resolver.rs @@ -0,0 +1,90 @@ +use collections::HashMap; +use core; +use {CargoResult}; + +#[allow(dead_code)] +pub fn resolve(deps: &Vec, registry: &core::Registry) -> CargoResult> { + let mut remaining = deps.clone(); + let mut resolve = HashMap::<&str, &core::Package>::new(); + + loop { + let curr = match remaining.pop() { + Some(curr) => curr, + None => return Ok(resolve.values().map(|v| (*v).clone()).collect()) + }; + + let opts = registry.query(curr.get_name()); + + assert!(!resolve.contains_key_equiv(&curr.get_name()), "already traversed {}", curr.get_name()); + // Temporary, but we must have exactly one option to satisfy the dep + assert!(opts.len() == 1, "invalid num of results {}", opts.len()); + + let pkg = opts.get(0); + resolve.insert(pkg.get_name(), *pkg); + } +} + +#[cfg(test)] +mod test { + + use hamcrest::{ + assert_that, + equal_to, + of_len, + contains + }; + + use core::{ + MemRegistry, + Dependency, + Package + }; + + use super::{ + resolve + }; + + fn pkg(name: &str) -> Package { + Package::new(name, &Vec::::new()) + } + + fn dep(name: &str) -> Dependency { + Dependency::new(name) + } + + fn registry(pkgs: Vec) -> MemRegistry { + MemRegistry::new(&pkgs) + } + + #[test] + pub fn test_resolving_empty_dependency_list() { + let res = resolve(&vec!(), ®istry(vec!())).unwrap(); + + assert_that(&res, equal_to(&Vec::::new())); + } + + #[test] + pub fn test_resolving_only_package() { + let reg = registry(vec!(pkg("foo"))); + let res = resolve(&vec!(dep("foo")), ®); + + assert_that(&res.unwrap(), equal_to(&vec!(pkg("foo")))); + } + + #[test] + pub fn test_resolving_one_dep() { + let reg = registry(vec!(pkg("foo"), pkg("bar"))); + let res = resolve(&vec!(dep("foo")), ®); + + assert_that(&res.unwrap(), equal_to(&vec!(pkg("foo")))); + } + + #[test] + pub fn test_resolving_multiple_deps() { + let reg = registry(vec!(pkg("foo"), pkg("bar"), pkg("baz"))); + let res = resolve(&vec!(dep("foo"), dep("baz")), ®).unwrap(); + + assert_that(&res, of_len(2)); + assert_that(&res, contains(vec!(pkg("foo"), pkg("baz"))).exactly()); + } +} diff --git a/src/cargo/mod.rs b/src/cargo/mod.rs index 43701c701..c26e4b412 100644 --- a/src/cargo/mod.rs +++ b/src/cargo/mod.rs @@ -3,8 +3,12 @@ #![allow(deprecated_owned_vector)] -extern crate serialize; +extern crate collections; extern crate hammer; +extern crate serialize; + +#[cfg(test)] +extern crate hamcrest; use serialize::{Decoder,Encoder,Decodable,Encodable,json}; use std::io; @@ -12,6 +16,7 @@ use std::fmt; use std::fmt::{Show,Formatter}; use hammer::{FlagDecoder,FlagConfig,HammerError}; + pub mod core; pub mod util; diff --git a/tests/support.rs b/tests/support.rs index bfce65254..ac5d6587d 100644 --- a/tests/support.rs +++ b/tests/support.rs @@ -161,7 +161,7 @@ struct Execs { impl Execs { - pub fn with_stdout(mut self, expected: &str) -> Execs { + pub fn with_stdout(mut ~self, expected: &str) -> ~Execs { self.expect_stdout = Some(expected.to_owned()); self } @@ -204,7 +204,7 @@ impl ham::SelfDescribing for Execs { } impl ham::Matcher for Execs { - fn matches(&self, process: &ProcessBuilder) -> ham::MatchResult { + fn matches(&self, process: ProcessBuilder) -> ham::MatchResult { let res = process.exec_with_output(); match res { @@ -214,8 +214,8 @@ impl ham::Matcher for Execs { } } -pub fn execs() -> Execs { - Execs { +pub fn execs() -> ~Execs { + ~Execs { expect_stdout: None, expect_stdin: None, expect_exit_code: None diff --git a/tests/test_cargo_compile.rs b/tests/test_cargo_compile.rs index 02b1cc4a3..c30e997e0 100644 --- a/tests/test_cargo_compile.rs +++ b/tests/test_cargo_compile.rs @@ -32,7 +32,7 @@ test!(cargo_compile_with_explicit_manifest_path { assert_that(&p.root().join("target/foo"), existing_file()); assert_that( - &cargo::util::process("foo").extra_path(p.root().join("target")), + cargo::util::process("foo").extra_path(p.root().join("target")), execs().with_stdout("i am foo\n")); }) -- 2.30.2